package com.demo;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 注意：类很多代码在上面已经书写过详细注释，这里写过的注释不再书写
 * @author xiyang
 *
 */
public class ThreadWaitTest {
	
	private static Semaphore semaphore = new Semaphore(5);// 设置并发信号量为5
	private static AtomicInteger atomicInteger = new AtomicInteger(0);// 声明原子操作整数，初始化为9
	// 这句代码详细注释见主函数，另外我这里为了方便直接设置参数为10，实际开发可以根据需求实例化
	private static CountDownLatch cdl = new CountDownLatch(10);
	
	public static void main(String[] args) throws InterruptedException {
		
		// 因为我们需要处理主线程等待所有子线程运行结束之后再运行，
		// 因此为了方便，科学，我们再次介绍一个线程并发工具包CountDownLatch，
		// 同样，我们需要它的一个有参构造，CountDownLatch cdl = new CountDownLatch(count);
		// 注意，这里参数，必须和线程池的最大数一致，也就是说，这里的参数必须是你线程运行的最大数
		// 比如我这里要运行10个线程，并发5个，那么，这里的count==10
		// 因为我们需要在内部类中使用，所以声明为成员变量

		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					// 注意：在刚进入方法就需要获得一把锁，再次强调，是刚进入方法就获得一把锁
					try {
						semaphore.acquire();// 获取一把锁，因为是在匿名内部类中使用，所以需要将其声明为成员变量
						int current = atomicInteger.getAndIncrement();
						System.out.println("第" + current +"进来了");// 打印日志
						Thread.sleep(1000);// 让当前线程睡1秒，模仿耗时操作
						System.out.println("第" + current +"出去了");
						cdl.countDown();// 此方法的意思是线程数量-1，要在当前线程执行完毕之后书写，或者说子线程核心代码执行完毕之后
						semaphore.release();// 释放一把锁，必须释放，必须是方法最后一步
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
		
		cdl.await();// 主线程等待，当线程数量为0时主线程执行
		System.out.println("主线程执行");
		// 如此就解决了线程并发问题和主线程等待所有子线程运行完毕之后运行

	}
}
